package com.by.datasource.config;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.by.datasource.datapermission.DataPermissionContext;
import com.by.datasource.datapermission.DataPermissionSupport;
import com.by.datasource.dynamictablename.DyTNContext;
import com.by.datasource.dynamictablename.algorithm.AbstractDyTNAlgorithm;
import com.by.datasource.dynamictablename.algorithm.DyTNAlgorithmContext;
import com.by.datasource.handler.ByDataPermissionHandler;
import com.by.datasource.handler.ByMetaObjectHandler;
import com.by.datasource.interceptor.ByDataPermissionInterceptor;
import com.by.datasource.interceptor.BySqlAuditInterceptor;
import com.by.datasource.properties.ByDatasourceProperties;
import com.by.datasource.tenant.TCorpCode;
import com.by.datasource.tenant.TCorpCodeContext;
import com.by.datasource.tenant.TProjectId;
import com.by.datasource.tenant.TProjectIdContext;
import com.by.toolkit.consts.ByContextConst;
import com.by.toolkit.platform.ByContext;
import com.by.toolkit.utils.ByIfNotEmptyUtil;
import com.by.toolkit.utils.ByIfTrueUtil;
import com.by.toolkitspring.customizeannotation.ByAnnotationContext;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.NullValue;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Objects;

/**
 * mybatisPlus配置
 *
 * @author YanBingHao
 * @since 2025/9/29
 */
@Slf4j
@Configuration
@MapperScan("com.by.**.mapper")
@ConditionalOnProperty(name = "by.datasource.enabled", havingValue = "true")
public class ByMybatisPlusConfig {

    @Resource
    private ByDatasourceProperties byDatasourceProperties;

    @Resource
    private ByAnnotationContext byAnnotationContext;

    private static final Expression NULL_EXPRESSION = new NullValue();

    @Bean
    public MybatisPlusInterceptor initInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();

        // 多租户插件
        ByIfNotEmptyUtil.doIfNotEmpty(byAnnotationContext.getAnnotationInfos(TProjectId.class)
                , () -> {
                    mybatisPlusInterceptor.addInnerInterceptor(projectIdInterceptor());
                });

        ByIfNotEmptyUtil.doIfNotEmpty(byAnnotationContext.getAnnotationInfos(TCorpCode.class)
                , () -> {
                    mybatisPlusInterceptor.addInnerInterceptor(corpCodeInterceptor());
                });
        // 动态表名插件
        ByIfTrueUtil.doIfTrue(byDatasourceProperties.isDyTableName(), () -> {
            mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInterceptor());
        });
        // 数据权限
        ByIfTrueUtil.doIfTrue(byDatasourceProperties.isDataPermission(), () -> {
            mybatisPlusInterceptor.addInnerInterceptor(dataPermissionInterceptor());
        });
        // 分页插件
        mybatisPlusInterceptor.addInnerInterceptor(paginationInterceptor());
        // 阻止恶意的全表更新删除
        mybatisPlusInterceptor.addInnerInterceptor(blockAttackInterceptor());
        // sql审计
        //mybatisPlusInterceptor.addInnerInterceptor(auditInterceptor());
        return mybatisPlusInterceptor;
    }

    public PaginationInnerInterceptor paginationInterceptor() {
        PaginationInnerInterceptor interceptor = new PaginationInnerInterceptor();
        interceptor.setDbType(DbType.MYSQL);
        // 分查询最大数量
        interceptor.setMaxLimit(5000L);
        // 优化left join
        interceptor.setOptimizeJoin(true);
        return interceptor;
    }

    public BlockAttackInnerInterceptor blockAttackInterceptor() {
        return new BlockAttackInnerInterceptor();
    }

    public BySqlAuditInterceptor auditInterceptor() {
        return new BySqlAuditInterceptor();
    }

    public TenantLineInnerInterceptor projectIdInterceptor() {
        TProjectIdContext TProjectIdContext = SpringUtil.getBean(TProjectIdContext.class);
        TenantLineInnerInterceptor interceptor = new TenantLineInnerInterceptor();
        interceptor.setTenantLineHandler(new TenantLineHandler() {
            @Override
            public Expression getTenantId() {
                Long projectId = ByContext.getProjectId();
                //log.info("[ByMybatisPlusConfig][projectIdInterceptor] projectId:[{}]", projectId);
                if (Objects.isNull(projectId)) {
                    return NULL_EXPRESSION;
                }
                return new LongValue(projectId);
            }

            @Override
            public String getTenantIdColumn() {
                return "t_project_id";
            }

            @Override
            public boolean ignoreTable(String tableName) {
                return ByContext.invalidFlag(ByContextConst.PROJECT_ID)
                        || ByContext.getIgnoreTenant()
                        || !TProjectIdContext.projectIdFlag(tableName);

            }
        });
        return interceptor;
    }

    public TenantLineInnerInterceptor corpCodeInterceptor() {
        TCorpCodeContext TCorpCodeContext = SpringUtil.getBean(TCorpCodeContext.class);
        TenantLineInnerInterceptor interceptor = new TenantLineInnerInterceptor();
        interceptor.setTenantLineHandler(new TenantLineHandler() {
            @Override
            public Expression getTenantId() {
                Long corpCode = ByContext.getCorpCode();
                //log.info("[ByMybatisPlusConfig][corpCodeInterceptor] corpCode:[{}]", corpCode);
                if (Objects.isNull(corpCode)) {
                    return NULL_EXPRESSION;
                }
                return new LongValue(corpCode);
            }

            @Override
            public String getTenantIdColumn() {
                return "t_corp_code";
            }

            @Override
            public boolean ignoreTable(String tableName) {
                return ByContext.invalidFlag(ByContextConst.CORP_CODE)
                        || ByContext.getIgnoreTenant()
                        || !TCorpCodeContext.corpCodeFlag(tableName);
            }
        });
        return interceptor;
    }

    public ByDataPermissionInterceptor dataPermissionInterceptor() {
        DataPermissionContext dataPermissionContext = SpringUtil.getBean(DataPermissionContext.class);
        DataPermissionSupport dataPermissionSupport = SpringUtil.getBean(DataPermissionSupport.class);
        return new ByDataPermissionInterceptor(new ByDataPermissionHandler(dataPermissionContext, dataPermissionSupport)
                .injectDpSupport());
    }

    public DynamicTableNameInnerInterceptor dynamicTableNameInterceptor() {
        DyTNContext dyTNContext = SpringUtil.getBean(DyTNContext.class);
        DyTNAlgorithmContext dyTNAlgorithmContext = SpringUtil.getBean(DyTNAlgorithmContext.class);

        DynamicTableNameInnerInterceptor interceptor = new DynamicTableNameInnerInterceptor();
        interceptor.setTableNameHandler((sql, tableName) -> {
            String newTableName = tableName.contains(".") ? StringUtils.split(tableName, ".")[1] : tableName;
            //log.info("[ByMybatisPlusConfig][dynamicTableNameInterceptor] sql:[{}] tableName:[{}] newTableName:[{}]", sql, tableName, newTableName);
            if (!dyTNContext.dynamicTableName(newTableName)
                    || dyTNContext.disabledFlag()
                    || ByContext.getIgnoreDyTableName().contains(tableName)
            ) {
                return tableName;
            }
            Integer algorithm = dyTNContext.dynamicTableNameAlgorithm(newTableName);
            AbstractDyTNAlgorithm abstractDyTNAlgorithm = dyTNAlgorithmContext.algorithmInstance(algorithm);
            return abstractDyTNAlgorithm.dynamic(tableName);
        });
        return interceptor;
    }

    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new ByMetaObjectHandler();
    }

}
